/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


// r_render.cpp -- back-end rendering


#include "r_local.h"


/*
 ==============================================================================

 Z-FILL RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_RenderDepth
 ==================
*/
static void RB_RenderDepth (void){

	stage_t	*stage;
	bool	hasAlphaTest = false;
	int		i;

	RB_Deform();

	RB_SetMaterialState();

	qglVertexPointer(3, GL_FLOAT, sizeof(glVertex_t), POINTER_XYZ(backEnd.vertexPointer));

	if (backEnd.material->coverage == MC_PERFORATED){
		for (i = 0, stage = backEnd.material->stages; i < backEnd.material->numStages; i++, stage++){
			if (!backEnd.material->expressionRegisters[stage->conditionRegister])
				continue;

			if (stage->shaderStage.program)
				continue;

			if (!(stage->flags & SF_ALPHATEST))
				continue;

			hasAlphaTest = true;

			if ((backEnd.material->flags & MF_POLYGONOFFSET) || (stage->flags & SF_POLYGONOFFSET)){
				GL_Enable(GL_POLYGON_OFFSET_FILL);
				GL_PolygonOffset(r_offsetFactor->floatValue, r_offsetUnits->floatValue);
			}
			else
				GL_Disable(GL_POLYGON_OFFSET_FILL);

			GL_Enable(GL_ALPHA_TEST);
			GL_AlphaFunc(GL_GREATER, backEnd.material->expressionRegisters[stage->alphaTestRegister]);

			RB_SetupTextureStage(&stage->textureStage);
			RB_SetupColorStage(&stage->colorStage);

			RB_DrawElements();

			RB_CleanupColorStage(&stage->colorStage);
			RB_CleanupTextureStage(&stage->textureStage);
		}

		if (hasAlphaTest)
			return;
	}

	GL_Disable(GL_ALPHA_TEST);

	GL_DisableTexture();

	RB_DrawElements();
}

/*
 ==================
 RB_FillDepthBuffer
 ==================
*/
void RB_FillDepthBuffer (int numDrawMeshes, drawMesh_t *drawMeshes){

	drawMesh_t	*drawMesh;
	uint		sort = 0;
	material_t	*material, *oldMaterial = NULL;
	entity_t	*entity, *oldEntity = NULL;
	meshType_t	type;
	bool		caps;
	bool		skip;
	int			i;

	if (!numDrawMeshes)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_FillDepthBuffer ----------\n");

	// Set the render function
	backEnd.renderBatch = RB_RenderDepth;

	// Set the GL state
	GL_Disable(GL_BLEND);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_LEQUAL);

	GL_Disable(GL_STENCIL_TEST);

	GL_ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
	GL_DepthMask(GL_TRUE);
	GL_StencilMask(0);

	// Run through the meshes
	for (i = 0, drawMesh = drawMeshes; i < numDrawMeshes; i++, drawMesh++){
		// Check if the state changed
		if (drawMesh->sort != sort){
			sort = drawMesh->sort;

			// Render the last batch
			RB_EndBatch();

			// Decompose sort
			R_DecomposeSort(backEnd.scene, sort, &material, &entity, &type, &caps);

			// Stop if it's translucent
			if (material->coverage == MC_TRANSLUCENT)
				break;

			// Evaluate registers if needed
			if (material != oldMaterial || entity != oldEntity)
				R_EvaluateRegisters(material, backEnd.time, entity->e.materialParms, backEnd.materialParms);

			// Skip if condition evaluated to false
			if (!material->expressionRegisters[material->conditionRegister]){
				skip = true;
				continue;
			}

			// Set the entity state if needed
			if (entity != oldEntity)
				RB_SetEntityState(entity);

			// Create a new batch
			RB_BeginBatch(material, entity, false, false);

			oldMaterial = material;
			oldEntity = entity;

			skip = false;
		}

		if (skip)
			continue;

		// Batch the mesh
		RB_BatchMesh(type, drawMesh->mesh);
	}

	// Render the last batch
	RB_EndBatch();
}


/*
 ==============================================================================

 AMBIENT RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_RenderMaterial
 ==================
*/
static void RB_RenderMaterial (void){

	stage_t	*stage;
	int		i;

	if (r_logFile->integerValue)
		QGL_LogPrintf("----- RB_RenderMaterial ( %s ) -----\n", backEnd.material->name);

	RB_Deform();

	RB_SetMaterialState();

	qglVertexPointer(3, GL_FLOAT, sizeof(glVertex_t), POINTER_XYZ(backEnd.vertexPointer));

	for (i = 0, stage = backEnd.material->stages; i < backEnd.material->numStages; i++, stage++){
		if (!backEnd.material->expressionRegisters[stage->conditionRegister])
			continue;

		if (stage->lighting != SL_AMBIENT)
			continue;

		if (backEnd.viewParms.bloom){
			if (!stage->isGlow)
				continue;
		}

		if (stage->flags & SF_BLEND){
			if (stage->blendSrc == GL_ZERO && stage->blendDst == GL_ONE)
				continue;
		}

		RB_SetStageState(stage);

		if (stage->shaderStage.program){
			if (!r_shaderPrograms->integerValue)
				continue;

			RB_SetupShaderStage(&stage->shaderStage);

			RB_DrawElements();

			RB_CleanupShaderStage(&stage->shaderStage);
		}
		else {
			RB_SetupTextureStage(&stage->textureStage);
			RB_SetupColorStage(&stage->colorStage);

			RB_DrawElements();

			RB_CleanupColorStage(&stage->colorStage);
			RB_CleanupTextureStage(&stage->textureStage);
		}
	}
}

/*
 ==================
 RB_RenderMaterialPasses
 ==================
*/
void RB_RenderMaterialPasses (int numDrawMeshes, drawMesh_t *drawMeshes, bool postProcess){

	drawMesh_t	*drawMesh;
	uint		sort = 0;
	material_t	*material, *oldMaterial = NULL;
	entity_t	*entity, *oldEntity = NULL;
	meshType_t	type;
	bool		caps;
	bool		skip;
	int			i;

	if (!numDrawMeshes)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_RenderMaterialPasses ----------\n");

	// Development tools
	if (r_skipAmbient->integerValue){
		if (!postProcess)
			return;
	}

	if (r_skipPostProcess->integerValue){
		if (postProcess)
			return;
	}

	// Copy current render if needed
	if (postProcess)
		RB_CopyFrameBuffer(tr.currentRenderTexture);

	// Set the render function
	backEnd.renderBatch = RB_RenderMaterial;

	// Set the GL state
	GL_Enable(GL_DEPTH_TEST);

	GL_Disable(GL_STENCIL_TEST);

	if (postProcess)
		GL_DepthMask(GL_TRUE);
	else
		GL_DepthMask(GL_FALSE);

	GL_StencilMask(0);

	// Run through the meshes
	for (i = 0, drawMesh = drawMeshes; i < numDrawMeshes; i++, drawMesh++){
		// Check if the state changed
		if (drawMesh->sort != sort){
			sort = drawMesh->sort;

			// Render the last batch
			RB_EndBatch();

			// Decompose sort
			R_DecomposeSort(backEnd.scene, sort, &material, &entity, &type, &caps);

			// Development tool
			if (r_skipTranslucent->integerValue){
				if (material->coverage == MC_TRANSLUCENT)
					break;
			}

			// Skip if it doesn't have glow stages when rendering a bloom view
			if (backEnd.viewParms.bloom && !material->hasGlowStages){
				skip = true;
				continue;
			}

			// Skip if it doesn't have ambient stages
			if (!material->numAmbientStages){
				skip = true;
				continue;
			}

			// Evaluate registers if needed
			if (material != oldMaterial || entity != oldEntity)
				R_EvaluateRegisters(material, backEnd.time, entity->e.materialParms, backEnd.materialParms);

			// Skip if condition evaluated to false
			if (!material->expressionRegisters[material->conditionRegister]){
				skip = true;
				continue;
			}

			// Set the entity state if needed
			if (entity != oldEntity)
				RB_SetEntityState(entity);

			// Create a new batch
			RB_BeginBatch(material, entity, false, false);

			oldMaterial = material;
			oldEntity = entity;

			skip = false;
		}

		if (skip)
			continue;

		// Batch the mesh
		RB_BatchMesh(type, drawMesh->mesh);
	}

	// Render the last batch
	RB_EndBatch();
}


/*
 ==============================================================================

 STENCIL SHADOW & LIGHT INTERACTION RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_RenderStencilShadow
 ==================
*/
static void RB_RenderStencilShadow (void){

	qglVertexPointer(4, GL_FLOAT, sizeof(glShadowVertex_t), POINTER_XYZW(backEnd.vertexPointer));

	if (backEnd.shadowCaps){
		qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
		qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
	}
	else {
		qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
		qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
	}

	RB_DrawElements();
}

/*
 ==================
 RB_StencilShadowPass
 ==================
*/
static void RB_StencilShadowPass (int numDrawMeshes, drawMesh_t *drawMeshes){

	drawMesh_t	*drawMesh;
	uint		sort = 0;
	material_t	*material, *oldMaterial = NULL;
	entity_t	*entity, *oldEntity = NULL;
	meshType_t	type;
	bool		caps;
	bool		skip;
	int			i;

	if (!numDrawMeshes)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_StencilShadowPass ----------\n");

	// Development tool
	if (r_skipShadows->integerValue)
		return;

	// Set the render function
	backEnd.renderBatch = RB_RenderStencilShadow;

	// Set the GL state
	GL_DisableTexture();

	GL_Disable(GL_CULL_FACE);

	GL_Enable(GL_POLYGON_OFFSET_FILL);
	GL_PolygonOffset(r_shadowOffsetFactor->floatValue, r_shadowOffsetUnits->floatValue);

	GL_Disable(GL_BLEND);

	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_LEQUAL);

	GL_Enable(GL_STENCIL_TEST);
	GL_StencilFunc(GL_ALWAYS, 128, 255);
	GL_StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	GL_ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(255);

	// Bind the program
	qglUseProgram(tr.shadowProgram->programId);

	// Run through the meshes
	for (i = 0, drawMesh = drawMeshes; i < numDrawMeshes; i++, drawMesh++){
		// Check if the state changed
		if (drawMesh->sort != sort){
			sort = drawMesh->sort;

			// Render the last batch
			RB_EndBatch();

			// Decompose sort
			R_DecomposeSort(backEnd.scene, sort, &material, &entity, &type, &caps);

			// Evaluate registers if needed
			if (material != oldMaterial || entity != oldEntity)
				R_EvaluateRegisters(material, backEnd.time, entity->e.materialParms, backEnd.materialParms);

			// Skip if condition evaluated to false
			if (!material->expressionRegisters[material->conditionRegister]){
				skip = true;
				continue;
			}

			// Set the entity state if needed
			if (entity != oldEntity){
				RB_SetEntityState(entity);

				// Set the light state for this entity
				RB_SetLightState(entity, backEnd.light, NULL, NULL);

				// Set up the program uniform
				qglUniform3fv(backEnd.shadowParms.lightOrigin, 1, backEnd.localParms.lightOrigin.ToFloatPtr());
			}

			// Create a new batch
			RB_BeginBatch(material, entity, true, caps);

			oldMaterial = material;
			oldEntity = entity;

			skip = false;
		}

		if (skip)
			continue;

		// Batch the mesh
		RB_BatchMeshShadow(type, drawMesh->mesh);
	}

	// Render the last batch
	RB_EndBatch();

	// Restore the GL state
	qglStencilOpSeparate(GL_FRONT_AND_BACK, GL_KEEP, GL_KEEP, GL_KEEP);

	// Unbind the program
	qglUseProgram(0);
}

/*
 ==================
 RB_RenderInteraction
 ==================
*/
static void RB_RenderInteraction (stage_t *bumpStage, stage_t *diffuseStage, stage_t *specularStage){

	texture_t		*bumpTexture, *diffuseTexture, *specularTexture;
	odMat4			bumpMatrix, diffuseMatrix, specularMatrix;
	odVec3			lightColor, diffuseColor, specularColor;
	float			colorScale, colorBias;
	float			specularExponent, specularBrightness;
	vertexColor_t	vertexColor = VC_IGNORE;

	// Set up the light color
	if (r_lightScale->floatValue > 1.0f){
		lightColor[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]] * r_lightScale->floatValue;
		lightColor[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]] * r_lightScale->floatValue;
		lightColor[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]] * r_lightScale->floatValue;
	}
	else {
		lightColor[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]];
		lightColor[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]];
		lightColor[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]];
	}

	// Set up color scale and bias
	if (diffuseStage && specularStage){
		if (diffuseStage->colorStage.vertexColor == VC_MODULATE && specularStage->colorStage.vertexColor == VC_MODULATE)
			vertexColor = VC_MODULATE;

		if (diffuseStage->colorStage.vertexColor == VC_INVERSE_MODULATE && specularStage->colorStage.vertexColor == VC_INVERSE_MODULATE)
			vertexColor = VC_INVERSE_MODULATE;
	}
	else {
		if (diffuseStage)
			vertexColor = diffuseStage->colorStage.vertexColor;

		if (specularStage)
			vertexColor = specularStage->colorStage.vertexColor;
	}

	switch (vertexColor){
	case VC_IGNORE:
		colorScale = 0.0f;
		colorBias = 1.0f;

		break;
	case VC_MODULATE:
		colorScale = 1.0f;
		colorBias = 0.0f;

		break;
	case VC_INVERSE_MODULATE:
		colorScale = -1.0f;
		colorBias = 1.0f;

		break;
	}

	// Development tools
	if (r_skipBump->integerValue)
		bumpStage = NULL;
	if (r_skipDiffuse->integerValue)
		diffuseStage = NULL;
	if (r_skipSpecular->integerValue)
		specularStage = NULL;

	// Set up the bump texture and matrix
	if (bumpStage){
		bumpTexture = bumpStage->textureStage.texture;

		RB_MultiplyTextureMatrix(backEnd.material, &bumpStage->textureStage, bumpMatrix);
	}
	else {
		bumpTexture = tr.flatTexture;

		bumpMatrix.Identity();
	}

	// Set up the diffuse texture, matrix, and color
	if (diffuseStage){
		diffuseTexture = diffuseStage->textureStage.texture;

		RB_MultiplyTextureMatrix(backEnd.material, &diffuseStage->textureStage, diffuseMatrix);

		diffuseColor[0] = lightColor[0] * backEnd.material->expressionRegisters[diffuseStage->colorStage.registers[0]];
		diffuseColor[1] = lightColor[1] * backEnd.material->expressionRegisters[diffuseStage->colorStage.registers[1]];
		diffuseColor[2] = lightColor[2] * backEnd.material->expressionRegisters[diffuseStage->colorStage.registers[2]];
	}
	else {
		diffuseTexture = tr.whiteTexture;

		diffuseMatrix.Identity();

		diffuseColor = lightColor;
	}

	// Set up the specular texture, matrix, color, exponent, and brightness
	if (specularStage){
		specularTexture = specularStage->textureStage.texture;

		RB_MultiplyTextureMatrix(backEnd.material, &specularStage->textureStage, specularMatrix);

		specularColor[0] = lightColor[0] * backEnd.material->expressionRegisters[specularStage->colorStage.registers[0]];
		specularColor[1] = lightColor[1] * backEnd.material->expressionRegisters[specularStage->colorStage.registers[1]];
		specularColor[2] = lightColor[2] * backEnd.material->expressionRegisters[specularStage->colorStage.registers[2]];

		specularExponent = specularStage->specularExponent;
		specularBrightness = specularStage->specularBrightness;
	}
	else {
		specularTexture = tr.blackTexture;

		specularMatrix.Identity();

		specularColor = lightColor;

		specularExponent = 16.0f;
		specularBrightness = 2.0f;
	}

	// Set up the program uniforms and textures
	if (backEnd.lightMaterial->lightType == LT_GENERIC){
		qglUniform3fv(backEnd.interactionParms.viewOrigin, 1, backEnd.localParms.viewOrigin.ToFloatPtr());
		qglUniform3fv(backEnd.interactionParms.lightOrigin, 1, backEnd.localParms.lightOrigin.ToFloatPtr());
		qglUniform1f(backEnd.interactionParms.colorScale, colorScale);
		qglUniform1f(backEnd.interactionParms.colorBias, colorBias);
		qglUniformMatrix4fv(backEnd.interactionParms.bumpMatrix, 1, true, bumpMatrix.ToFloatPtr());
		qglUniformMatrix4fv(backEnd.interactionParms.diffuseMatrix, 1, true, diffuseMatrix.ToFloatPtr());
		qglUniformMatrix4fv(backEnd.interactionParms.specularMatrix, 1, true, specularMatrix.ToFloatPtr());
		qglUniformMatrix4fv(backEnd.interactionParms.lightMatrix, 1, false, backEnd.localParms.lightMatrix.ToFloatPtr());
		qglUniform3fv(backEnd.interactionParms.diffuseColor, 1, diffuseColor.ToFloatPtr());
		qglUniform3fv(backEnd.interactionParms.specularColor, 1, specularColor.ToFloatPtr());
		qglUniform1f(backEnd.interactionParms.specularExponent, specularExponent);
		qglUniform1f(backEnd.interactionParms.specularBrightness, specularBrightness);

		GL_SelectTexture(0);
		GL_BindTexture(bumpTexture);

		GL_SelectTexture(1);
		GL_BindTexture(diffuseTexture);

		GL_SelectTexture(2);
		GL_BindTexture(specularTexture);

		GL_SelectTexture(3);
		GL_BindTexture(backEnd.lightStage->textureStage.texture);

		GL_SelectTexture(4);
		GL_BindTexture(backEnd.lightMaterial->lightFalloffImage);
	}
	else {
		qglUniform1f(backEnd.ambientLightParms.colorScale, colorScale);
		qglUniform1f(backEnd.ambientLightParms.colorBias, colorBias);
		qglUniformMatrix4fv(backEnd.ambientLightParms.bumpMatrix, 1, true, bumpMatrix.ToFloatPtr());
		qglUniformMatrix4fv(backEnd.ambientLightParms.diffuseMatrix, 1, true, diffuseMatrix.ToFloatPtr());
		qglUniformMatrix4fv(backEnd.ambientLightParms.lightMatrix, 1, false, backEnd.localParms.lightMatrix.ToFloatPtr());
		qglUniform3fv(backEnd.ambientLightParms.diffuseColor, 1, diffuseColor.ToFloatPtr());

		GL_SelectTexture(0);
		GL_BindTexture(bumpTexture);

		GL_SelectTexture(1);
		GL_BindTexture(diffuseTexture);

		GL_SelectTexture(2);
		GL_BindTexture(backEnd.lightStage->textureStage.texture);

		GL_SelectTexture(3);
		GL_BindTexture(backEnd.lightMaterial->lightFalloffImage);
	}

	// Draw it
	RB_DrawElements();
}

/*
 ==================
 RB_RenderLightInteractions
 ==================
*/
static void RB_RenderLightInteractions (void){

	stage_t	*bumpStage = NULL, *diffuseStage = NULL, *specularStage = NULL;
	stage_t	*stage;
	int		i;

	if (r_logFile->integerValue)
		QGL_LogPrintf("----- RB_RenderLightInteractions ( %s on %s ) -----\n", backEnd.lightMaterial->name, backEnd.material->name);

	RB_SetMaterialState();

	qglVertexPointer(3, GL_FLOAT, sizeof(glVertex_t), POINTER_XYZ(backEnd.vertexPointer));
	qglNormalPointer(GL_FLOAT, sizeof(glVertex_t), POINTER_NORMAL(backEnd.vertexPointer));
	qglTexCoordPointer(2, GL_FLOAT, sizeof(glVertex_t), POINTER_ST(backEnd.vertexPointer));
	qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(glVertex_t), POINTER_COLOR(backEnd.vertexPointer));
	qglVertexAttribPointer(10, 3, GL_FLOAT, false, sizeof(glVertex_t), POINTER_TANGENT0(backEnd.vertexPointer));
	qglVertexAttribPointer(11, 3, GL_FLOAT, false, sizeof(glVertex_t), POINTER_TANGENT1(backEnd.vertexPointer));

	for (i = 0, stage = backEnd.material->stages; i < backEnd.material->numStages; i++, stage++){
		if (!backEnd.material->expressionRegisters[stage->conditionRegister])
			continue;

		if (stage->lighting == SL_AMBIENT)
			continue;

		switch (stage->lighting){
		case SL_BUMP:
			if (bumpStage)
				RB_RenderInteraction(bumpStage, diffuseStage, specularStage);
			else {
				if (diffuseStage || specularStage)
					Com_Printf(S_COLOR_YELLOW "Material '%s' has a bump stage with a preceeding diffuse/specular stage\n", backEnd.material->name);
			}

			bumpStage = stage;
			diffuseStage = NULL;
			specularStage = NULL;

			break;
		case SL_DIFFUSE:
			if (diffuseStage){
				if (bumpStage)
					RB_RenderInteraction(bumpStage, diffuseStage, specularStage);
				else
					Com_Printf(S_COLOR_YELLOW "Material '%s' has a diffuse stage without a preceeding bump stage\n", backEnd.material->name);
			}

			diffuseStage = stage;

			break;
		case SL_SPECULAR:
			if (specularStage){
				if (bumpStage)
					RB_RenderInteraction(bumpStage, diffuseStage, specularStage);
				else
					Com_Printf(S_COLOR_YELLOW "Material '%s' has a specular stage without a preceeding bump stage\n", backEnd.material->name);
			}

			specularStage = stage;

			break;
		}
	}

	RB_RenderInteraction(bumpStage, diffuseStage, specularStage);
}

/*
 ==================
 RB_LightInteractionPass
 ==================
*/
static void RB_LightInteractionPass (int numDrawMeshes, drawMesh_t *drawMeshes){

	drawMesh_t	*drawMesh;
	uint		sort = 0;
	material_t	*material, *oldMaterial = NULL;
	entity_t	*entity, *oldEntity = NULL;
	meshType_t	type;
	bool		caps;
	bool		skip;
	int			i, j;

	if (!numDrawMeshes)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_LightInteractionPass ----------\n");

	// Set the render function
	backEnd.renderBatch = RB_RenderLightInteractions;

	// Set the GL state
	GL_Enable(GL_BLEND);
	GL_BlendFunc(GL_ONE, GL_ONE);

	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_EQUAL);

	if (!r_skipShadows->integerValue && backEnd.light->castShadows){
		GL_Enable(GL_STENCIL_TEST);
		GL_StencilFunc(GL_GEQUAL, 128, 255);
		GL_StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	}
	else {
		GL_Enable(GL_STENCIL_TEST);
		GL_StencilFunc(GL_ALWAYS, 128, 255);
		GL_StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	}

	GL_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(255);

	// Bind the program
	if (backEnd.lightMaterial->lightType == LT_GENERIC)
		qglUseProgram(tr.interactionProgram->programId);
	else
		qglUseProgram(tr.ambientLightProgram->programId);

	// Enable the arrays
	qglEnableClientState(GL_NORMAL_ARRAY);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglEnableClientState(GL_COLOR_ARRAY);

	qglEnableVertexAttribArray(10);
	qglEnableVertexAttribArray(11);

	// Run through the light stages
	for (i = 0, backEnd.lightStage = backEnd.lightMaterial->stages; i < backEnd.lightMaterial->numStages; i++, backEnd.lightStage++){
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->conditionRegister])
			continue;

		// Run through the meshes
		for (j = 0, drawMesh = drawMeshes; j < numDrawMeshes; j++, drawMesh++){
			// Check if the state changed
			if (drawMesh->sort != sort){
				sort = drawMesh->sort;

				// Render the last batch
				RB_EndBatch();

				// Decompose sort
				R_DecomposeSort(backEnd.scene, sort, &material, &entity, &type, &caps);

				// Evaluate registers if needed
				if (material != oldMaterial || entity != oldEntity)
					R_EvaluateRegisters(material, backEnd.time, entity->e.materialParms, backEnd.materialParms);

				// Skip if condition evaluated to false
				if (!material->expressionRegisters[material->conditionRegister]){
					skip = true;
					continue;
				}

				// Set the entity state if needed
				if (entity != oldEntity){
					RB_SetEntityState(entity);

					// Set the light state for this entity
					RB_SetLightState(entity, backEnd.light, backEnd.lightMaterial, backEnd.lightStage);
				}

				// Create a new batch
				RB_BeginBatch(material, entity, false, false);

				oldMaterial = material;
				oldEntity = entity;

				skip = false;
			}

			if (skip)
				continue;

			// Batch the mesh
			RB_BatchMesh(type, drawMesh->mesh);
		}

		// Render the last batch
		RB_EndBatch();
	}

	// Restore the GL state
	GL_SelectTexture(0);

	// Disable the arrays
	qglDisableVertexAttribArray(11);
	qglDisableVertexAttribArray(10);

	qglDisableClientState(GL_COLOR_ARRAY);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisableClientState(GL_NORMAL_ARRAY);

	// Unbind the program
	qglUseProgram(0);
}

/*
 ==================
 RB_RenderLights
 ==================
*/
void RB_RenderLights (int numDrawLights, drawLight_t *drawLights){

	int		i;

	if (backEnd.viewParms.bloom)
		return;

	if (!numDrawLights)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_RenderLights ----------\n");

	// Development tool
	if (r_skipInteractions->integerValue)
		return;

	// Run through the lights
	for (i = 0, backEnd.light = drawLights; i < numDrawLights; i++, backEnd.light++){
		// Set the light material
		backEnd.lightMaterial = backEnd.light->material;

		// Evaluate registers
		R_EvaluateRegisters(backEnd.lightMaterial, backEnd.time, backEnd.light->materialParms, backEnd.materialParms);

		// Skip if condition evaluated to false
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightMaterial->conditionRegister])
			continue;

		// Set the scissor rectangle
		qglScissor(backEnd.light->scissorX, backEnd.light->scissorY, backEnd.light->scissorWidth, backEnd.light->scissorHeight);

		// Set the depth bounds
		if (glConfig.depthBoundsTestAvailable)
			qglDepthBoundsEXT(backEnd.light->depthMin, backEnd.light->depthMax);

		// Clear the stencil buffer if needed
		if (!r_skipShadows->integerValue && backEnd.light->castShadows){
			GL_StencilMask(255);

			qglClearStencil(128);
			qglClear(GL_STENCIL_BUFFER_BIT);
		}

		// Render selfShadow meshes to the stencil buffer
		RB_StencilShadowPass(backEnd.light->numShadowMeshes[0], backEnd.light->shadowMeshes[0]);

		// Render noSelfShadow meshes with lighting
		RB_LightInteractionPass(backEnd.light->numInteractionMeshes[1], backEnd.light->interactionMeshes[1]);

		// Render noSelfShadow meshes to the stencil buffer
		RB_StencilShadowPass(backEnd.light->numShadowMeshes[1], backEnd.light->shadowMeshes[1]);

		// Render selfShadow meshes with lighting
		RB_LightInteractionPass(backEnd.light->numInteractionMeshes[0], backEnd.light->interactionMeshes[0]);
	}

	// Restore the scissor rectangle
	qglScissor(backEnd.viewParms.x, backEnd.viewParms.y, backEnd.viewParms.width, backEnd.viewParms.height);

	// Restore the depth bounds
	if (glConfig.depthBoundsTestAvailable)
		qglDepthBoundsEXT(0.0f, 1.0f);
}


/*
 ==============================================================================

 FOG & BLEND LIGHT RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_RenderEmptyFogLightVolume
 ==================
*/
static void RB_RenderEmptyFogLightVolume (void){

	int		cornerIndices[6][4] = {{3, 2, 6, 7}, {0, 1, 5, 4}, {2, 3, 1, 0}, {4, 5, 7, 6}, {1, 3, 7, 5}, {2, 0, 4, 6}};
	odVec3	color;
	int		i, j;

	// Set the GL state
	GL_Enable(GL_CULL_FACE);
	GL_CullFace(GL_BACK);

	GL_Disable(GL_POLYGON_OFFSET_FILL);

	GL_Enable(GL_BLEND);
	GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_LEQUAL);

	GL_Disable(GL_STENCIL_TEST);

	GL_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(0);

	GL_SelectTexture(0);
	GL_LoadIdentity(GL_TEXTURE);
	GL_TexEnv(GL_MODULATE);

	GL_Enable(GL_TEXTURE_GEN_S);
	GL_Enable(GL_TEXTURE_GEN_T);

	GL_TexGen(GL_S, GL_OBJECT_LINEAR);
	GL_TexGen(GL_T, GL_OBJECT_LINEAR);

	GL_SelectTexture(1);
	GL_LoadIdentity(GL_TEXTURE);
	GL_TexEnv(GL_MODULATE);

	GL_Enable(GL_TEXTURE_GEN_S);
	GL_Enable(GL_TEXTURE_GEN_T);

	GL_TexGen(GL_S, GL_OBJECT_LINEAR);
	GL_TexGen(GL_T, GL_OBJECT_LINEAR);

	// Set the entity state
	RB_SetEntityState(backEnd.scene->worldEntity);

	// Run through the light stages
	for (i = 0, backEnd.lightStage = backEnd.lightMaterial->stages; i < backEnd.lightMaterial->numStages; i++, backEnd.lightStage++){
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->conditionRegister])
			continue;

		// Set up the color
		color[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]];
		color[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]];
		color[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]];

		qglColor3fv(color.ToFloatPtr());

		// Set up the textures
		GL_SelectTexture(0);
		GL_EnableTexture(backEnd.lightStage->textureStage.texture->target);
		GL_BindTexture(backEnd.lightStage->textureStage.texture);

		GL_SelectTexture(1);
		GL_EnableTexture(backEnd.lightMaterial->lightFalloffImage->target);
		GL_BindTexture(backEnd.lightMaterial->lightFalloffImage);

		// Set the light state for this entity
		RB_SetLightState(backEnd.scene->worldEntity, backEnd.light, backEnd.lightMaterial, backEnd.lightStage);

		// Render the fog volume
		qglBegin(GL_QUADS);
		for (j = 0; j < 6; j++){
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][0]].ToFloatPtr());
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][1]].ToFloatPtr());
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][2]].ToFloatPtr());
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][3]].ToFloatPtr());
		}
		qglEnd();

		// Check for errors
		if (!r_ignoreGLErrors->integerValue)
			GL_CheckForErrors();
	}

	// Restore the GL state
	GL_SelectTexture(1);
	GL_DisableTexture();

	GL_Disable(GL_TEXTURE_GEN_S);
	GL_Disable(GL_TEXTURE_GEN_T);

	GL_SelectTexture(0);

	GL_Disable(GL_TEXTURE_GEN_S);
	GL_Disable(GL_TEXTURE_GEN_T);
}

/*
 ==================
 RB_RenderStencilFogLightVolume
 ==================
*/
static void RB_RenderStencilFogLightVolume (void){

	int		cornerIndices[6][4] = {{3, 2, 6, 7}, {0, 1, 5, 4}, {2, 3, 1, 0}, {4, 5, 7, 6}, {1, 3, 7, 5}, {2, 0, 4, 6}};
	int		i;

	// Set the GL state
	GL_LoadMatrix(GL_MODELVIEW, backEnd.viewParms.modelviewMatrix);

	GL_DisableTexture();

	GL_Disable(GL_CULL_FACE);

	GL_Disable(GL_POLYGON_OFFSET_FILL);

	GL_Disable(GL_BLEND);

	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_LEQUAL);

	GL_Enable(GL_STENCIL_TEST);
	GL_StencilFunc(GL_ALWAYS, 128, 255);
	GL_StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	GL_ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(255);

	GL_DepthRange(0.0f, 1.0f);

	// Clear the stencil buffer
	qglClearStencil(128);
	qglClear(GL_STENCIL_BUFFER_BIT);

	// Draw it
	qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
	qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP);

	qglBegin(GL_QUADS);
	for (i = 0; i < 6; i++){
		qglVertex3fv(backEnd.light->data.corners[cornerIndices[i][0]].ToFloatPtr());
		qglVertex3fv(backEnd.light->data.corners[cornerIndices[i][1]].ToFloatPtr());
		qglVertex3fv(backEnd.light->data.corners[cornerIndices[i][2]].ToFloatPtr());
		qglVertex3fv(backEnd.light->data.corners[cornerIndices[i][3]].ToFloatPtr());
	}
	qglEnd();

	// Restore the GL state
	qglStencilOpSeparate(GL_FRONT_AND_BACK, GL_KEEP, GL_KEEP, GL_KEEP);

	// Check for errors
	if (!r_ignoreGLErrors->integerValue)
		GL_CheckForErrors();
}

/*
 ==================
 RB_RenderFogLight
 ==================
*/
static void RB_RenderFogLight (void){

	RB_SetMaterialState();

	qglVertexPointer(3, GL_FLOAT, sizeof(glVertex_t), POINTER_XYZ(backEnd.vertexPointer));

	RB_DrawElements();
}

/*
 ==================
 RB_FogLightPass
 ==================
*/
static void RB_FogLightPass (int numDrawMeshes, drawMesh_t *drawMeshes){

	drawMesh_t	*drawMesh;
	uint		sort = 0;
	material_t	*material, *oldMaterial = NULL;
	entity_t	*entity, *oldEntity = NULL;
	meshType_t	type;
	bool		caps;
	bool		skip;
	int			cornerIndices[6][4] = {{3, 2, 6, 7}, {0, 1, 5, 4}, {2, 3, 1, 0}, {4, 5, 7, 6}, {1, 3, 7, 5}, {2, 0, 4, 6}};
	odVec3		color;
	int			i, j;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_FogLightPass ----------\n");

	if (!numDrawMeshes){
		RB_RenderEmptyFogLightVolume();
		return;
	}

	// Render the fog volume to the stencil buffer
	RB_RenderStencilFogLightVolume();

	// Set the render function
	backEnd.renderBatch = RB_RenderFogLight;

	// Set the GL state
	GL_Enable(GL_BLEND);
	GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_EQUAL);

	GL_Enable(GL_STENCIL_TEST);
	GL_StencilFunc(GL_LESS, 128, 255);
	GL_StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	GL_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(255);

	GL_SelectTexture(0);
	GL_LoadIdentity(GL_TEXTURE);
	GL_TexEnv(GL_MODULATE);

	GL_Enable(GL_TEXTURE_GEN_S);
	GL_Enable(GL_TEXTURE_GEN_T);

	GL_TexGen(GL_S, GL_OBJECT_LINEAR);
	GL_TexGen(GL_T, GL_OBJECT_LINEAR);

	GL_SelectTexture(1);
	GL_LoadIdentity(GL_TEXTURE);
	GL_TexEnv(GL_MODULATE);

	GL_Enable(GL_TEXTURE_GEN_S);
	GL_Enable(GL_TEXTURE_GEN_T);

	GL_TexGen(GL_S, GL_OBJECT_LINEAR);
	GL_TexGen(GL_T, GL_OBJECT_LINEAR);

	// Run through the light stages
	for (i = 0, backEnd.lightStage = backEnd.lightMaterial->stages; i < backEnd.lightMaterial->numStages; i++, backEnd.lightStage++){
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->conditionRegister])
			continue;

		// Set up the color
		color[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]];
		color[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]];
		color[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]];

		qglColor3fv(color.ToFloatPtr());

		// Set up the textures
		GL_SelectTexture(0);
		GL_EnableTexture(backEnd.lightStage->textureStage.texture->target);
		GL_BindTexture(backEnd.lightStage->textureStage.texture);

		GL_SelectTexture(1);
		GL_EnableTexture(backEnd.lightMaterial->lightFalloffImage->target);
		GL_BindTexture(backEnd.lightMaterial->lightFalloffImage);

		// Run through the meshes
		for (j = 0, drawMesh = drawMeshes; j < numDrawMeshes; j++, drawMesh++){
			// Check if the state changed
			if (drawMesh->sort != sort){
				sort = drawMesh->sort;

				// Render the last batch
				RB_EndBatch();

				// Decompose sort
				R_DecomposeSort(backEnd.scene, sort, &material, &entity, &type, &caps);

				// Evaluate registers if needed
				if (material != oldMaterial || entity != oldEntity)
					R_EvaluateRegisters(material, backEnd.time, entity->e.materialParms, backEnd.materialParms);

				// Skip if condition evaluated to false
				if (!material->expressionRegisters[material->conditionRegister]){
					skip = true;
					continue;
				}

				// Set the entity state if needed
				if (entity != oldEntity){
					RB_SetEntityState(entity);

					// Set the light state for this entity
					RB_SetLightState(entity, backEnd.light, backEnd.lightMaterial, backEnd.lightStage);
				}

				// Create a new batch
				RB_BeginBatch(material, entity, false, false);

				oldMaterial = material;
				oldEntity = entity;

				skip = false;
			}

			if (skip)
				continue;

			// Batch the mesh
			RB_BatchMesh(type, drawMesh->mesh);
		}

		// Render the last batch
		RB_EndBatch();
	}

	// Set the GL state
	GL_Enable(GL_CULL_FACE);
	GL_CullFace(GL_BACK);

	GL_Disable(GL_POLYGON_OFFSET_FILL);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_LEQUAL);

	GL_Disable(GL_STENCIL_TEST);

	// Set the entity state
	RB_SetEntityState(backEnd.scene->worldEntity);

	// Run through the light stages
	for (i = 0, backEnd.lightStage = backEnd.lightMaterial->stages; i < backEnd.lightMaterial->numStages; i++, backEnd.lightStage++){
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->conditionRegister])
			continue;

		// Set up the color
		color[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]];
		color[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]];
		color[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]];

		qglColor3fv(color.ToFloatPtr());

		// Set up the textures
		GL_SelectTexture(0);
		GL_EnableTexture(backEnd.lightStage->textureStage.texture->target);
		GL_BindTexture(backEnd.lightStage->textureStage.texture);

		GL_SelectTexture(1);
		GL_EnableTexture(backEnd.lightMaterial->lightFalloffImage->target);
		GL_BindTexture(backEnd.lightMaterial->lightFalloffImage);

		// Set the light state for this entity
		RB_SetLightState(backEnd.scene->worldEntity, backEnd.light, backEnd.lightMaterial, backEnd.lightStage);

		// Render the fog volume
		qglBegin(GL_QUADS);
		for (j = 0; j < 6; j++){
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][0]].ToFloatPtr());
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][1]].ToFloatPtr());
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][2]].ToFloatPtr());
			qglVertex3fv(backEnd.light->data.corners[cornerIndices[j][3]].ToFloatPtr());
		}
		qglEnd();

		// Check for errors
		if (!r_ignoreGLErrors->integerValue)
			GL_CheckForErrors();
	}

	// Restore the GL state
	GL_SelectTexture(1);
	GL_DisableTexture();

	GL_Disable(GL_TEXTURE_GEN_S);
	GL_Disable(GL_TEXTURE_GEN_T);

	GL_SelectTexture(0);

	GL_Disable(GL_TEXTURE_GEN_S);
	GL_Disable(GL_TEXTURE_GEN_T);
}

/*
 ==================
 RB_RenderBlendLight
 ==================
*/
static void RB_RenderBlendLight (void){

	RB_SetMaterialState();

	qglVertexPointer(3, GL_FLOAT, sizeof(glVertex_t), POINTER_XYZ(backEnd.vertexPointer));

	RB_DrawElements();
}

/*
 ==================
 RB_BlendLightPass
 ==================
*/
static void RB_BlendLightPass (int numDrawMeshes, drawMesh_t *drawMeshes){

	drawMesh_t	*drawMesh;
	uint		sort = 0;
	material_t	*material, *oldMaterial = NULL;
	entity_t	*entity, *oldEntity = NULL;
	meshType_t	type;
	bool		caps;
	bool		skip;
	odVec4		color;
	int			i, j;

	if (!numDrawMeshes)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_BlendLightPass ----------\n");

	// Development tool
	if (r_skipBlendLights->integerValue)
		return;

	// Set the render function
	backEnd.renderBatch = RB_RenderBlendLight;

	// Set the GL state
	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_EQUAL);

	GL_Disable(GL_STENCIL_TEST);

	GL_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(0);

	GL_SelectTexture(0);
	GL_LoadIdentity(GL_TEXTURE);
	GL_TexEnv(GL_MODULATE);

	GL_Enable(GL_TEXTURE_GEN_S);
	GL_Enable(GL_TEXTURE_GEN_T);
	GL_Enable(GL_TEXTURE_GEN_Q);

	GL_TexGen(GL_S, GL_OBJECT_LINEAR);
	GL_TexGen(GL_T, GL_OBJECT_LINEAR);
	GL_TexGen(GL_Q, GL_OBJECT_LINEAR);

	GL_SelectTexture(1);
	GL_LoadIdentity(GL_TEXTURE);
	GL_TexEnv(GL_MODULATE);

	qglTexCoord2f(0.0f, 0.5f);

	GL_Enable(GL_TEXTURE_GEN_S);

	GL_TexGen(GL_S, GL_OBJECT_LINEAR);

	// Run through the light stages
	for (i = 0, backEnd.lightStage = backEnd.lightMaterial->stages; i < backEnd.lightMaterial->numStages; i++, backEnd.lightStage++){
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->conditionRegister])
			continue;

		// Set the GL state
		if (backEnd.lightStage->flags & SF_BLEND){
			if (backEnd.lightStage->blendSrc == GL_ZERO && backEnd.lightStage->blendDst == GL_ONE)
				continue;

			GL_Enable(GL_BLEND);
			GL_BlendFunc(backEnd.lightStage->blendSrc, backEnd.lightStage->blendDst);
		}
		else
			GL_Disable(GL_BLEND);

		// Set up the color
		color[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]];
		color[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]];
		color[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]];
		color[3] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[3]];

		qglColor4fv(color.ToFloatPtr());

		// Set up the textures
		GL_SelectTexture(0);
		GL_EnableTexture(backEnd.lightStage->textureStage.texture->target);
		GL_BindTexture(backEnd.lightStage->textureStage.texture);

		GL_SelectTexture(1);
		GL_EnableTexture(backEnd.lightMaterial->lightFalloffImage->target);
		GL_BindTexture(backEnd.lightMaterial->lightFalloffImage);

		// Run through the meshes
		for (j = 0, drawMesh = drawMeshes; j < numDrawMeshes; j++, drawMesh++){
			// Check if the state changed
			if (drawMesh->sort != sort){
				sort = drawMesh->sort;

				// Render the last batch
				RB_EndBatch();

				// Decompose sort
				R_DecomposeSort(backEnd.scene, sort, &material, &entity, &type, &caps);

				// Evaluate registers if needed
				if (material != oldMaterial || entity != oldEntity)
					R_EvaluateRegisters(material, backEnd.time, entity->e.materialParms, backEnd.materialParms);

				// Skip if condition evaluated to false
				if (!material->expressionRegisters[material->conditionRegister]){
					skip = true;
					continue;
				}

				// Set the entity state if needed
				if (entity != oldEntity){
					RB_SetEntityState(entity);

					// Set the light state for this entity
					RB_SetLightState(entity, backEnd.light, backEnd.lightMaterial, backEnd.lightStage);
				}

				// Create a new batch
				RB_BeginBatch(material, entity, false, false);

				oldMaterial = material;
				oldEntity = entity;

				skip = false;
			}

			if (skip)
				continue;

			// Batch the mesh
			RB_BatchMesh(type, drawMesh->mesh);
		}

		// Render the last batch
		RB_EndBatch();
	}

	// Restore the GL state
	GL_SelectTexture(1);
	GL_DisableTexture();

	GL_Disable(GL_TEXTURE_GEN_S);

	GL_SelectTexture(0);

	GL_Disable(GL_TEXTURE_GEN_S);
	GL_Disable(GL_TEXTURE_GEN_T);
	GL_Disable(GL_TEXTURE_GEN_Q);
}

/*
 ==================
 RB_RenderFogLights
 ==================
*/
void RB_RenderFogLights (int numDrawLights, drawLight_t *drawLights){

	int		i;

	if (backEnd.viewParms.bloom)
		return;

	if (!numDrawLights)
		return;

	if (r_logFile->integerValue)
		QGL_LogPrintf("---------- RB_RenderFogLights ----------\n");

	// Development tool
	if (r_skipFogLights->integerValue)
		return;

	// Run through the lights
	for (i = 0, backEnd.light = drawLights; i < numDrawLights; i++, backEnd.light++){
		// Set the light material
		backEnd.lightMaterial = backEnd.light->material;

		// Evaluate registers
		R_EvaluateRegisters(backEnd.lightMaterial, backEnd.time, backEnd.light->materialParms, backEnd.materialParms);

		// Skip if condition evaluated to false
		if (!backEnd.lightMaterial->expressionRegisters[backEnd.lightMaterial->conditionRegister])
			continue;

		// Set the scissor rectangle
		qglScissor(backEnd.light->scissorX, backEnd.light->scissorY, backEnd.light->scissorWidth, backEnd.light->scissorHeight);

		// Set the depth bounds
		if (glConfig.depthBoundsTestAvailable)
			qglDepthBoundsEXT(backEnd.light->depthMin, backEnd.light->depthMax);

		// Render meshes with lighting
		if (backEnd.lightMaterial->lightType == LT_FOG)
			RB_FogLightPass(backEnd.light->numInteractionMeshes[0], backEnd.light->interactionMeshes[0]);
		else
			RB_BlendLightPass(backEnd.light->numInteractionMeshes[0], backEnd.light->interactionMeshes[0]);
	}

	// Restore the scissor rectangle
	qglScissor(backEnd.viewParms.x, backEnd.viewParms.y, backEnd.viewParms.width, backEnd.viewParms.height);

	// Restore the depth bounds
	if (glConfig.depthBoundsTestAvailable)
		qglDepthBoundsEXT(0.0f, 1.0f);
}


/*
 ==============================================================================

 2D RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_RenderOverlayMaterial
 ==================
*/
void RB_RenderOverlayMaterial (void){

	stage_t	*stage;
	int		i;

	if (r_logFile->integerValue)
		QGL_LogPrintf("----- RB_RenderOverlayMaterial ( %s ) -----\n", backEnd.material->name);

	// Copy current render if needed
	if (backEnd.material->sort == SORT_POST_PROCESS)
		RB_CopyFrameBuffer(tr.currentRenderTexture);

	RB_SetMaterialState();

	qglVertexPointer(3, GL_FLOAT, sizeof(glVertex_t), POINTER_XYZ(backEnd.vertexPointer));

	for (i = 0, stage = backEnd.material->stages; i < backEnd.material->numStages; i++, stage++){
		if (!backEnd.material->expressionRegisters[stage->conditionRegister])
			continue;

		if (stage->flags & SF_BLEND){
			if (stage->blendSrc == GL_ZERO && stage->blendDst == GL_ONE)
				continue;
		}

		RB_SetStageState(stage);

		if (stage->shaderStage.program){
			if (!r_shaderPrograms->integerValue)
				continue;

			RB_SetupShaderStage(&stage->shaderStage);

			RB_DrawElements();

			RB_CleanupShaderStage(&stage->shaderStage);
		}
		else {
			RB_SetupTextureStage(&stage->textureStage);
			RB_SetupColorStage(&stage->colorStage);

			RB_DrawElements();

			RB_CleanupColorStage(&stage->colorStage);
			RB_CleanupTextureStage(&stage->textureStage);
		}
	}
}
